package org.shangda.wechat.pet.util;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 用于DC接口加密解密的方法
 */
public class SignUtil {
	
	private static final Logger log = LoggerFactory.getLogger(SignUtil.class);

    //解密密钥(自行随机生成)
    public static final String KEY = "SECRET128BIT_KEY";//密钥key


    /**
     * 加密方法示例
     *
     * 1.随机生成iv，并且使用base64编码，生成 ivBase64
     * 2.使用AES-128-CBC加密明文内容，生成 encryptedData
     * 3.使用SHA256生成对ivBase64 + encryptedData，生成mac
     *
     * @param content 待加密的明文
     * @return String 返回mac
     */
    public static String encrypt4DC(String content){
        try {
            String iv = generateRandomIV(16);
//            String iv = "TEST_ALPHA_IV_12";
            String encryptedData = encrypt(content, iv);
            String ivBase64 = new String(Base64.encodeBase64(iv.getBytes(),false));
            log.info("encrypt4DC ivBase64: {}", ivBase64);
            String data = ivBase64 + encryptedData;
            log.info("encrypt data:" + data);
            String mac = getSHA256Str(data);
            log.info("encrypt4DC mac: {}", mac);
            return mac;
        } catch (Exception e) {
            log.error("encrypt4DC exception! ",e);
            throw new RuntimeException("encrypt4DC exception",e);
        }
    }

    /**
     * 用于给DC的解密
     * @param ivBase64
     * @param encryptedData
     * @return
     */
    public static String decrypt4DC(String ivBase64, String encryptedData){
        try {
            String iv = new String(new BASE64Decoder().decodeBuffer(ivBase64),"utf-8");
            log.info("decrypt4DC iv: {}", iv);
            String data = decrypt(encryptedData, iv);
            log.info("decrypt4DC data: {}", ivBase64);
            return data;
        } catch (Exception e) {
            log.error("decrypt4DC exception",e);
            throw new RuntimeException("decrypt4DC exception",e);
        }
    }

    /**
     * AES-128-CBC加密
     * @param content
     * @param iv
     * @return
     */
    public static String encrypt(String content, String iv){
        try {
            byte[] raw = KEY.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");//"算法/模式/补码方式"
            //使用CBC模式，需要一个向量iv，可增加加密算法的强度
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes("utf-8"));
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ips);
            byte[] encrypted = cipher.doFinal(content.getBytes("utf-8"));
            String data = new String(Base64.encodeBase64(encrypted, false));
            log.info("encrypt data: {}",data);
            return data;
        } catch (Exception e) {
            log.error("encrypt exception:", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * AES-128-CBC解密
     * @param content String encrypt data
     * @param iv String
     * @return
     */
    public static String decrypt(String content, String iv) {
        try {
            byte[] raw = KEY.getBytes("utf-8");
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ips);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(content);

            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original);
            return originalString;
        } catch (Exception ex) {
            log.error("decrypt exception:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 生成固定数量的随机字符
     * @param len int 随机字符的数量
     * @return String
     */
    public static String generateRandomIV(int len){
        if(len <= 0){
            throw new RuntimeException("invalid len");
        }
        StringBuffer result = new StringBuffer();
        String ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for(int i=0; i<len; i++){
            result.append(ch.charAt(ThreadLocalRandom.current().nextInt(ch.length())));
        }
        return result.toString();
    }

    /**
     * SHA256摘要加密方法
     * @param str
     * @return String
     */
    public static String getSHA256Str(String str){
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(KEY.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(str.getBytes());
            return byteArrayToHexString(bytes);
        } catch (Exception e) {
            log.error("getSHA256Str excpetion!", e);
            throw new RuntimeException("getSHA256Str excpetion!", e);
        }
    }

    /**
     * 检查参数mac是否正确
     * @param ivBase64
     * @param content
     * @param macParam
     * @return
     */
    public static boolean check(String ivBase64, String content, String macParam){
        try {
            log.info("check ivBase64:{}, content:{}, macParam:{}",ivBase64, content, macParam);
            String iv = new String(new BASE64Decoder().decodeBuffer(ivBase64),"utf-8");
            log.info("check iv:{}",iv);
            String encryptedData = encrypt(content, iv);
            String data = ivBase64 + encryptedData;
            String mac = getSHA256Str(data);
            log.info("check new mac: {}", mac);
            return mac.equals(macParam);
        } catch (Exception e) {
            log.error("check exception:", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 将加密后的字节数组转换成字符串
     *
     * @param b
     *            字节数组
     * @return 字符串
     */
    private static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b != null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1) {
                hs.append('0');
            }
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }



    public static void main(String[] args) throws Exception {
        String content = "{\"wechatAppId\":\"wx7045ca7fd2b00805\"}";
        System.out.println(content);
        String mac=encrypt4DC(content);
        System.out.println("mac : "+ mac);

    }
}
